package top.someapp.applib.cast;

import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Map;

/**
 * @author zw-zheng
 * Created on 2019-07-20
 */
public class DefaultCastor {

    private static final Map<Type, Castor> castorMap = new HashMap<>();
    private static final Map<Type, Castor> customerCastors = new Hashtable<>();

    static {
        castorMap.put(Byte.TYPE, new ByteCastor());
        castorMap.put(Short.TYPE, new ShortCastor());
        castorMap.put(Integer.TYPE, new IntCastor());
        castorMap.put(Long.TYPE, new LongCastor());
        castorMap.put(Float.TYPE, new FloatCastor());
        castorMap.put(Double.TYPE, new DoubleCastor());
        castorMap.put(Boolean.TYPE, new BooleanCastor());
        castorMap.put(Character.TYPE, new CharCastor());
        castorMap.put(String.class, new StringCastor());
    }

    private DefaultCastor() {
        // no need instance.
    }

    public static Byte toByte(Object from, Byte defaultValue) {
        return castTo(Byte.TYPE, from, defaultValue);
    }

    public static Short toShort(Object from, Short defaultValue) {
        return castTo(Short.TYPE, from, defaultValue);
    }

    public static Integer toInt(Object from, Integer defaultValue) {
        return castTo(Integer.TYPE, from, defaultValue);
    }

    public static Long toLong(Object from, Long defaultValue) {
        return castTo(Long.TYPE, from, defaultValue);
    }

    public static Float toFloat(Object from, Float defaultValue) {
        return castTo(Float.TYPE, from, defaultValue);
    }

    public static Double toDouble(Object from, Double defaultValue) {
        return castTo(Double.TYPE, from, defaultValue);
    }

    public static Boolean toBoolean(Object from, Boolean defaultValue) {
        return castTo(Boolean.TYPE, from, defaultValue);
    }

    public static Character toCharacter(Object from, Character defaultValue) {
        return castTo(Character.TYPE, from, defaultValue);
    }

    public static String toString(Object from, String defaultValue) {
        return castTo(String.class, from, defaultValue);
    }

    public static <T> T castTo(Type type, Object from, T defaultValue) {
        return castTo(type, from, defaultValue, customerCastors.get(type));
    }

    @SuppressWarnings("unchecked")
    public static <T> T castTo(Type type, Object from, T defaultValue, Castor castorIfFail) {
        T to = null;
        if (castorMap.containsKey(type)) {
            try {
                to = (T) castorMap.get(type).cast(from, defaultValue);
            } catch (ClassCastException e) {
                System.err.println(e.getMessage());
            }
        }
        if (to == null && castorIfFail != null) {
            try {
                to = (T) castorIfFail.cast(from, defaultValue);
            } catch (ClassCastException e) {
                System.err.println(e.getMessage());
            }
        }
        return to == null ? defaultValue : to;
    }

    public static void registerCastor(Type type, Castor castor) {
        customerCastors.put(type, castor);
    }

    private static class ByteCastor extends AbstractCastor<Byte> {

    }

    private static class ShortCastor extends AbstractCastor<Short> {

    }

    private static class IntCastor extends AbstractCastor<Integer> {

    }

    private static class LongCastor extends AbstractCastor<Long> {

    }

    private static class FloatCastor extends AbstractCastor<Float> {

    }

    private static class DoubleCastor extends AbstractCastor<Double> {

    }

    private static class BooleanCastor extends AbstractCastor<Boolean> {

    }

    private static class CharCastor extends AbstractCastor<Character> {

    }

    private static class StringCastor extends AbstractCastor<String> {

    }
}
